package com.demo.webapp.kcfinder.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.demo.common.Utils;
import com.demo.webapp.kcfinder.ActionMapping;
import com.demo.webapp.kcfinder.ActionMappingBuilder;
import com.demo.webapp.kcfinder.Dispatcher;
import com.demo.webapp.kcfinder.KCFinderStatics;
import com.demo.webapp.kcfinder.context.ActionContext;
import com.demo.webapp.kcfinder.utils.MvelValueStack;
import com.demo.webapp.kcfinder.utils.ValueContext;
import com.demo.webapp.kcfinder.utils.ValueStack;

/**
 * KCFinder的请求过滤器
 * 该请求通过调度器对象,分析和处理请求
 * @author ryuu.kk
 *
 */
public class KCFinderFilter implements Filter {

	private static final Log Logger = LogFactory.getLog(KCFinderFilter.class);
	
	/**
	 * FilterConfig
	 */
	private FilterConfig filterConfig;
	
	/**
	 * 调度器对象
	 */
	private Dispatcher dispatcher;
	
	/**
	 * ActionMapping生成器
	 */
	private List<ActionMappingBuilder> builders;
	
	protected boolean isAllowExecute = true;
	
	/**
	 * 初始化方法
	 * @param filterConfig FilterConfig
	 */
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
		builders = new ArrayList<ActionMappingBuilder>();
		
		dispatcher = (Dispatcher) filterConfig.getServletContext().getAttribute(KCFinderStatics.SERVLET_DISPATCHER);
		if (dispatcher == null) {
			throw new ServletException("KCFinderListener.class must be configure in web.xml.");
		}
		//配置ActionMapping的Builder对象
		String builderStr = filterConfig.getInitParameter(KCFinderStatics.SERVLET_CONFIGURATION_BUILDER);
		if (StringUtils.isEmpty(builderStr)) {
			builderStr = KCFinderStatics.DEFAULT_ACTIONMAPPING_BUILDER;
		}
		ActionMappingBuilder actionMappingBuilder = null;
		for (String builder : builderStr.split(",")) {
			try {
				ClassLoader loader = Thread.currentThread().getContextClassLoader();
				//loader = filterConfig.getServletContext().getClass().getClassLoader();
				actionMappingBuilder = loadClass(builder, loader != null ? loader : getClass().getClassLoader());
				//actionMappingBuilder = (ActionMappingBuilder) Class.forName(builder, true, getClass().getClassLoader()).newInstance();
			} catch (InstantiationException e) {
				Logger.error(e.getMessage());
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				Logger.error(e.getMessage());
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				Logger.error(e.getMessage());
				e.printStackTrace();
			}
			builders.add(actionMappingBuilder);
		}
		
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

		//初始化允许执行标志
		isAllowExecute = true;
		//执行前准备
		request = prepare(request, response);
		
		//do something
		
		if (isAllowExecute) {
			//执行
			execute(request, response);
		} else {
			chain.doFilter(request, response);
		}
	}

	/**
	 * 请求前处理方法
	 * 创建Dispatcher
	 * 创建ActionMapping
	 * 创建ActionContext
	 * 同时封装Request,为JSTL提供ActionContext,使其可以访问容器内部数据
	 * 
	 * @param servletRequest ServletRequest
	 * @param servletResponse ServletResponse
	 * @throws IOException 
	 */
	protected ServletRequest prepare(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {
		
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		
		ActionMapping mapping = null;
		
		for (ActionMappingBuilder builder : builders) {
			mapping = builder.getMapping(request);
			if (mapping != null) {
				break;
			}
		}
		if (mapping == null) {
			//该请求不被ActionMappingBuilder所支持,使用过滤器链继续执行请求
			isAllowExecute = false;
			return request;
		}
		//使用公共Dispatcher的配置信息,创建一个本地线程的Dispatcher
		Dispatcher threadDispatcher = new Dispatcher(filterConfig.getServletContext());
		threadDispatcher.setConfigurationPath(this.dispatcher.getConfigurationPath());
		threadDispatcher.setObjectFactory(this.dispatcher.getObjectFactory());
		threadDispatcher.setConfigurationManager(this.dispatcher.getConfigurationManager());
		threadDispatcher.setFreemarkerManager(this.dispatcher.getFreemarkerManager());
		Dispatcher.setInstance(threadDispatcher);
		
		//request
		//response
		//application
		//session
		//cookies
		//other
		//ActionContext create...
		ActionContext context = new ActionContext();

		Properties properties = dispatcher.getProperties();
		String strEncoding = properties.getProperty(KCFinderStatics.KCFINDER_KEY_CUSTOM_I18N_ENCODING);
		if (strEncoding == null || strEncoding.length() < 1) {
			strEncoding = "UTF-8";
		}
		
		//公共路径
		String strPath = properties.getProperty(KCFinderStatics.KCFINDER_KEY_PUBLIC_PATH);
		if (strPath == null || strPath.length() < 1) {
			strPath = "/public";
		}
		
		//kcfinder允许访问的路径
		String allowPaths = properties.getProperty(KCFinderStatics.KCFINDER_KEY_ALLOW_PATH);
		
		String strLocale = request.getParameter("lng");
		HttpSession session = request.getSession(true);
		Map<String, Object> params = new HashMap<String, Object>();
		Enumeration<?> enums = session.getAttributeNames();
		while (enums.hasMoreElements()) {
			String key = (String) enums.nextElement();
			params.put(key, session.getAttribute(key));
		}
		
		context.setSession(params);
		context.setCookies(request.getCookies());
		context.setLocale(Utils.localeFromString(strLocale, Locale.CHINESE));
		context.setActionMapping(mapping);
		//~~ -------------------------------------------------------------------------
		//  设置Request&Respongse操作需要在执行前进行
		//  原因是由于在准备期-执行期之间，有可能对HttpServletRequest进行了封装
		//  造成ActionContext容器的Request和Response与chain.doFilter(request,response)期间的请求不一致
		//
		//context.setRequest(request);
		//context.setResponse(response);
		//~~ -------------------------------------------------------------------------
		context.setServletContext(filterConfig.getServletContext());
		context.setEncoding(strEncoding);
		context.setPublishPath(strPath);
		// 允许的路径地址
		if (allowPaths == null || allowPaths.length() == 0) {
			// 默认只有公共路径
			allowPaths = strPath;
		}
		context.setAllowPath(allowPaths.split(","));
		
		ValueStack stack = new MvelValueStack(new ValueContext());
		context.setValueStack(stack);
		
		ActionContext.setContext(context);
		
		request.setAttribute(KCFinderStatics.SERVLET_REQUEST_ACTIONMAPPING, mapping);
		
		//封装Request
		request = threadDispatcher.wrapRequest(request, filterConfig.getServletContext());
		
		context.setRequest(request);
		context.setResponse(response);
		
		return servletRequest;
	};
	
	/**
	 * 请求处理方法
	 * 使用ActionMapping、ActionContext、Dispatcher处理请求
	 * @param request ServletRequest
	 * @param response ServletResponse
	 */
	protected void execute(ServletRequest request, ServletResponse response) {
		
		ActionMapping mapping = (ActionMapping) request.getAttribute(KCFinderStatics.SERVLET_REQUEST_ACTIONMAPPING);
		Dispatcher dispatcher = Dispatcher.getInstance();
		
		//将请求进行封装
		
		dispatcher.executeAction((HttpServletRequest) request, (HttpServletResponse) response, filterConfig.getServletContext(), mapping);
	};
	
	protected void sendError(ServletRequest servletRequest, ServletResponse servletResponse) {
		//
	}
	
	@SuppressWarnings("unchecked")
	private <T> T loadClass(String name, ClassLoader classloader) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		ClassLoader loader = classloader;
		//t = (T) Class.forName(name, true, loader).newInstance();
		Class<T> clazz = null;
		try {
			if (classloader == null) {
				return null;
			}
			clazz = (Class<T>) loader.loadClass(name);
		} catch (ClassNotFoundException e) {
			clazz = (Class<T>) loadClass(name, loader.getParent());
		}
		if (clazz == null) {
			return null;
		}
		return clazz.newInstance();
	}
	
	@Override
	public void destroy() {

		Dispatcher threadDispatcher = Dispatcher.getInstance();
		if (threadDispatcher != null) {
			threadDispatcher.cleanup();
		}
		if (dispatcher != null) {
			dispatcher.cleanup();
		}
	}
}
